package com.lauor.smpedr.core.executor;

import com.lauor.smpedr.Configuration;
import com.lauor.smpedr.plugin.InterceptorScopeEnum;
import com.lauor.smpedr.plugin.InterceptorWrapper;
import com.lauor.smpedr.plugin.Invocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * sql执行者代理
 */
public class ExecutorProxy implements InvocationHandler {

    private final static Logger LOG = LoggerFactory.getLogger(ExecutorProxy.class);
    //日志黑名称方法名，按需初始化
    private static Set<String> LOG_BLACK_LIST;

    private Executor executor;

    static {
        if ( LOG.isDebugEnabled() ){
            initBlackList();
        }
    }

    public ExecutorProxy(Executor executor) {
        this.executor = executor;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //执行拦截器
        if ( !invokeAround(method, args, true) ) return null;

        Object result = null;
        try {
            String methodName = method.getName();
            if ( needLog(methodName) ){
                long sTime = System.currentTimeMillis();
                result = method.invoke(this.executor, args);
                LOG.debug("{} cost {}ms", methodName, System.currentTimeMillis() - sTime);
            } else {
                result = method.invoke(this.executor, args);
            }
        } catch (Throwable throwable){
            if (throwable instanceof InvocationTargetException){
                throw ((InvocationTargetException) throwable).getTargetException();
            }
            throw throwable;
        }
        //执行拦截器
        invokeAround(method, args, false);
        return result;
    }

    private boolean invokeAround(Method method, Object[] args, boolean before){
        Configuration configuration = this.executor.getConfiguration();
        List<InterceptorWrapper> interceptorList = configuration.getInterceptorList();

        for (InterceptorWrapper interceptor : interceptorList) {
            Optional<List<InterceptorScopeEnum>> scopeEnums = interceptor.getScopes();
            if (scopeEnums.isEmpty() || !scopeEnums.get().contains( InterceptorScopeEnum.EXECUTOR )) {
                continue;
            }
            //校验方法名
            Optional<Set<String>> methods = interceptor.getTargetMethods();
            if (methods.isEmpty() || !methods.get().contains( method.getName() )){
                continue;
            }

            if (before){
                boolean flag = interceptor.getMeta().before( new Invocation(method, args) );
                if (!flag) {
                    return false;
                }
            } else {
                interceptor.getMeta().after( new Invocation(method, args) );
            }
        }
        return true;
    }
    //是否需要打印出执行日志
    private static boolean needLog(String methodName){
        return LOG_BLACK_LIST != null && !LOG_BLACK_LIST.contains(methodName);
    }
    //初始化日志黑名单
    private static void initBlackList(){
        LOG_BLACK_LIST = Stream.of("commit", "rollback", "close", "isClosed", "getConfiguration").collect(Collectors.toSet());
    }
}
